<?php
class Crawl_keyword_library extends Crawl_abstract{

    function __construct() {
        parent::__construct();
        $this->_ci->config->load('openplatforms', TRUE);
        $this->_platform = $this->_ci->input->post('platform');
        $this->_log_dir = $this->_log_dir.'/'.$this->_platform;
        $this->_result_log_dir = $this->_log_dir.'/'.$this->_result_log_dir;
    }

    /**
     * 使用代理IP抓取数据
     * @param $parameters             抓取使用的参数 array
     * @param string $use_proxy       代理IP模式 string  proxy(代理IP) or mobility(动态ADSL)
     * @param $crawl_from             抓取目标   string  (目标平台)
     * @param bool $return_temp_data  是否需要整理抓取的结果(如新浪手机版、云云手机版，如果每次抓取的数据都不完整，则以数组的形式返回每次抓取的数据)
     * @return array|string           返回抓取结果，成功 string 抓取数据不完整 array 抓取错误 array
     */
    public function crawl_use_proxy_ip($parameters, $use_proxy = self::PRROXY_IP, $crawl_from, $return_temp_data = FALSE) {
        $parameters['crawl_from'] = $crawl_from;
        //以 $use_proxy 指定的代理模式 开始抓取 即使用代理IP或者动态ADSL IP
        $try_crawl_use_proxy = self::get_content($parameters, $use_proxy);

        //验证抓取结果
        $process_result = $this->process_crawl_result($try_crawl_use_proxy, $use_proxy);
        $temp_array = array();
        //如果验证结果返回错误信息
        if(is_array($process_result) && array_key_exists($this->_error_string,$process_result) && $this->not_illegal_error($process_result)) {
            //抓取失败
            log_message(self::LOG_LEVEL_WARING, 'Try Crawl From '.$crawl_from.' Use '.$use_proxy.' Failed:'.json_encode($this->return_utf8_code($process_result)), FALSE, $this->_log_dir);
            //肉鸡抓取开始重试
            log_message(self::LOG_LEVEL_NOTICE, 'Will Crawl From '.$crawl_from.' Use Retry And Use '.$use_proxy.' With Parameters: '.json_encode($parameters), FALSE, $this->_log_dir);
            //如果需要整理抓取结果 腾讯、云云 以抓取的最多结果为准
            //返回的错误 错误码不可以是Cookie错误
            if($return_temp_data === TRUE && !in_array($process_result['error_code'], $this->_cookie_error_code) && (isset($crawl_result['result']) && $item_no = $this->get_item_no($try_crawl_use_proxy['result'])) != FALSE) {
                $temp_array[$item_no.'_t'] = $try_crawl_use_proxy['result'];
            }
            //尝试以 $use_proxy 指定的代理模式 重试
            $retry_result = $this->retry_crawl($parameters, $use_proxy, $crawl_from ,$return_temp_data);
            //如果重试存在错误
            if(is_array($retry_result) && array_key_exists($this->_error_string, $retry_result)) {
                log_message(self::LOG_LEVEL_WARING, 'Crawl From '.$crawl_from.' Use '.$use_proxy.' Failed:'.json_encode($this->return_utf8_code($retry_result)), FALSE, $this->_log_dir);
            } else if($return_temp_data === TRUE && is_array($retry_result) && count($retry_result) > 0) {
                //如果此平台在返回数据不完整的情况下 需要返回临时数组 返回结果是数组 并且不存在错误 并且返回的数组不为空
                ///如果需要整理抓取结果 重试返回结果为数组 并且不是空数组 腾讯、云云 以抓取的最多结果为准
                //合并第一次抓取 和重试抓取 返回的数组
                return array_merge($temp_array, $retry_result);
            } else {
                //返回重试抓取结果
                $log_string = is_array($retry_result) ? json_encode($this->return_utf8_code($retry_result)) : $retry_result;
                log_message(self::LOG_LEVEL_NOTICE, 'Retry Crawl '.$crawl_from.' Use '.$use_proxy.' IP result: '.$log_string, FALSE, $this->_result_log_dir);
            }

            return $retry_result;
        } else {
            //返回 肉鸡抓取结果
            $log_string = is_array($process_result) ? json_encode($this->return_utf8_code($process_result)) : $process_result;
            log_message(self::LOG_LEVEL_NOTICE, 'Crawl From '.$crawl_from.' Use Proxy IP result: '.$log_string, FALSE, $this->_result_log_dir);
            return $process_result;
        }
    }

    /**
     * 抓取重试
     * @param $parameters               抓取使用的参数 array
     * @param string $use_proxy         代理IP模式 string  proxy(代理IP) or mobility(动态ADSL)
     * @param $crawl_from               抓取目标   string  (目标平台)
     * @param bool $return_temp_data    是否需要整理抓取的结果(如新浪手机版、云云手机版，如果每次抓取的数据都不完整，则以数组的形式返回每次抓取的数据)
     * @return array|string             返回抓取结果，成功 string 抓取数据不完整 array 抓取错误 array
     */
    public function retry_crawl($parameters,$use_proxy = self::PRROXY_IP, $crawl_from, $return_temp_data = FALSE) {
        $retry_time = $this->_ci->config->item('try_times');
        if(!is_array($retry_time) || !array_key_exists($use_proxy, $retry_time)) {
            return $this->_ci->error_code_library->getError('system', 'params', 'no_must_parameter', 'ReTry Times');
        }
        $temp_array = array();
        for($i = 0; $i < $retry_time[$use_proxy]; $i++) {
            log_message(self::LOG_LEVEL_NOTICE, 'Will Retry '.($i+1).' Crawl '.$crawl_from.' Use '.$use_proxy.' With Parameters: '.json_encode($parameters), FALSE, $this->_log_dir);
            //以 $use_proxy 指定的代理模式 开始抓取 即使用代理IP或者动态ADSL IP
            $crawl_result = self::get_content($parameters, $use_proxy);
            $process_result = $this->process_crawl_result($crawl_result, $use_proxy);
            if(is_array($process_result) && array_key_exists($this->_error_string, $process_result) && $this->not_illegal_error($process_result)) {
                log_message('waring', 'Retry '.($i+1).' Crawl '.$crawl_from.' Use Proxy Failed: '.json_encode($this->return_utf8_code($process_result)), FALSE, $this->_log_dir);
                //腾讯、云云 以抓取的最多结果为准
                //如果此平台在返回数据不完整的情况下 需要返回临时数组 并且错误码不是cookie错误 并且存在抓取结果字符串 并且可以匹配到微博标签
                if($return_temp_data === TRUE && !in_array($process_result['error_code'], $this->_cookie_error_code) && (isset($crawl_result['result']) && $item_no = $this->get_item_no($crawl_result['result'])) != FALSE) {
                    $temp_array[$item_no.'_t'] = $crawl_result['result'];
                }
            } else {
                return $process_result;
            }
        }
        if($return_temp_data === TRUE && count($temp_array) > 0) {
            return $temp_array;
        }
        return $process_result;
    }

    /**
     * 抓取结果处理
     * 如果抓取结果验证失败 返回FALSE 并标记代理IP不可用
     * @param $crawl_result     抓取结果
     * @param string $use_proxy 使用的代理类型 self::PRROXY_IP 肉鸡 self::MOBILITY_IP 动态IP
     * @return bool | string    抓取失败返回 FALSE 抓取成功 返回抓取到的字符串
     */
    protected function process_crawl_result($crawl_result, $use_proxy = self::PRROXY_IP) {
        if(is_array($crawl_result) && array_key_exists($this->_error_string, $crawl_result)) {
            return $crawl_result;
        }

        //匹配“搜索无结果”字符串
        $match_null_tab_result = $this->match_null_tab($crawl_result['result']);
        if($match_null_tab_result !== FALSE) {
            return $match_null_tab_result;
        }
        $this->_proxy_ip = $crawl_result['use_ip'];
        $this->_use_proxy = $use_proxy;
        $validate_return_data_result = $this->validate_return_data($crawl_result['result']);

        if($validate_return_data_result === TRUE) {
            return $crawl_result['result'];
        } else if(array_key_exists($this->_error_string, $validate_return_data_result)) {
            return $validate_return_data_result;
        }
    }

    /**
     * 匹配空内容
     * @param $data
     * @return array|bool
     */
    public function match_null_tab($data) {
        if(!empty($this->_null_tab) && preg_match($this->_null_tab, $data)) {
            log_message(self::LOG_LEVEL_WARING, 'CURL Return Content Is Match Null Tab -- '.$this->_null_tab, FALSE, $this->_log_dir);
            return array('data' => NULL);
        } else {
            return FALSE;
        }
    }

    /**
     * 验证抓取到的数据
     * @param $data 抓取到的数据
     * @return bool 失败 FALSE 成功 TRUE
     */
    public function validate_return_data($data) {
        $match_html_result = $this->match_html($data);
        if(is_array($match_html_result) && array_key_exists($this->_error_string, $match_html_result)) {
            return $match_html_result;
        }else {
            return TRUE;
        }
    }

    /**
     * 正则验证抓取结果
     * 判断标标准20130524 如果能匹配到 验证码输入况 或者匹配不到HTML结束标签 认为 失败 否则 成功
     * @param $html  抓取到的数据
     * @return bool  失败 FALSE 成功 TRUE
     */
    public function match_html($html) { }

    /**
     * 获取含有抓取内容的数组中 匹配度最高的元素
     * @param array $all_result_array
     * @return mixed
     */
    public function get_max_item_no_data($all_result_array = array()) {
        krsort($all_result_array, SORT_NUMERIC);
        $key_array = array_keys($all_result_array);
        return $all_result_array[$key_array[0]];
    }

    /**
     * 合并抓取结果临时数组 并返回可以匹配最多数据的抓取到的html字符串
     * @param array $proxy_retry_result
     * @param array $try_result
     * @return bool|mixed
     */
    public function make_crawl_return_array($try_result = array(), $proxy_retry_result = array()) {
        $all_result = array();
        if(count($proxy_retry_result) > 0 && count($try_result) > 0) {
            //如果肉鸡重试返回了 不完整数据 的数组 将 临时数组合并
            $all_result = array_merge($proxy_retry_result, $try_result);
        } else {
            $all_result = $try_result;
        }
        if(count($all_result) > 0 && !array_key_exists('data', $all_result)) {
            return $this->get_max_item_no_data($all_result);
        } else {
            return $this->get_max_item_no_data($proxy_retry_result);;
        }
    }

    /**
     * 读取抓取的数据可以匹配到的信息数
     * @param $html
     * @return bool|int
     */
    public function get_item_no($html) {
        if(is_string($html)) {
            preg_match_all($this->_htmlsearch_config['tab_preg'], $html, $matches);
            if(isset($matches[0]) && count($matches[0]) > 0) {
                return count($matches[0]);
            } else {
                return FALSE;
            }
        } else {
            return FALSE;
        }
    }

    protected function get_page() {
        if($this->_ci->input->post('page') == FALSE || $this->_ci->input->post('page') < 1) {
            $page = 1;
        } else if($this->_ci->input->post('page') > $this->_ci->config->item('max_page')) {
        	//$page = $this->_ci->config->item('max_page');

        	//如果传入nopage_limit参数..则不受页数限定
        	$page = ($this->_ci->input->post('nopage_limit')) ? $this->_ci->input->post('page') : $this->_ci->config->item('max_page');
        } else {
            $page = trim($this->_ci->input->post('page'));
        }
        return $page;
    }

    public function save_crawl_result($result_string) {
        $this->_ci->load->model('keyword/Keyword_crawl_model');
        $save_cache_result = $this->_ci->Keyword_crawl_model->save_crawl_result($result_string);
        if($save_cache_result == FALSE) {
            log_message(self::LOG_LEVEL_WARING, 'Save Result To Redis Failed', FALSE, $this->_log_dir);
            $this->_ci->Keyword_crawl_model->save_in_db($result_string);
        }
    }

    public function get_basic_parameters($proxy_from) {
        $data['token']      = trim($this->_ci->input->post('token'));
        $data['key_word']   = trim($this->_ci->input->post('word'));
        $data['page']       = $this->get_page();
        $data['key_word_hash'] = md5($data['key_word']);
        $data['key_word_page_hash'] = md5($data['key_word'].$data['page']);
        $data['original_page'] = trim($this->_ci->input->post('page'));
        $data['platform'] = $this->_platform;
        $data['start_time'] = time();
        $data['ip_type'] = $proxy_from;
        return $data;
    }
}